package com.ndp.fb.mq.service.schedulerservice.adpublish;

import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.ndp.fb.enums.CampaignGroupResourceStatus;
import com.ndp.fb.enums.publish.PublishResultType;
import com.ndp.fb.enums.publish.PublishStatus;
import com.ndp.fb.fbagent.service.AdCampaignGroupFBService;
import com.ndp.fb.model.fbagent.param.APIPriority;
import com.ndp.fb.model.fbagent.param.ApiCallBackTarget;
import com.ndp.fb.model.fbagent.param.adcampaigngroups.AdCampaignParam;
import com.ndp.fb.model.fbagent.result.common.FBSingleId;
import com.ndp.fb.mongo.api.PublishCacheMongoService;
import com.ndp.fb.rdb.api.*;
import com.ndp.fb.rdb.model.AdCampaignGroup;
import com.ndp.fb.rdb.model.BaseAdLevel;
import com.ndp.fb.rdb.model.PublishBatch;
import com.ndp.fb.redis.model.AdCampaignGroupLevel;
import com.ndp.fb.util.ThreadPool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.HashMap;
import java.util.Map;

/**
 * Created by Jacky on 2015/3/12.
 */
@Component
public class PublishCampaignService extends PublishBase {

    public static final String AD_CAMPAIGN_GROUP = "adCampaignGroup";
    private static Logger logger = LoggerFactory.getLogger(PublishCampaignService.class);
    private static final String PUBLISH_CAMPAIGN = "PUBLISH_CAMPAIGN";
    @Reference
    private AdCampaignGroupFBService adCampaignGroupFBService;
    @Reference
    private AdCampaignRdbService adCampaignGroupRdbService;
    @Autowired
    private PublishAdSetService publishAdSetService;
    @Reference
    private PublishBatchRdbService publishBatchRdbService;
    @Reference
    private FbUserRdbService fbUserRdbService;
    @Reference
    private PublishResultRecordRdbService publishResultRecordRdbService;
    @Reference
    private PublishResultDetailRdbService publishResultDetailRdbService;


    @Autowired
    private PublishCacheMongoServiceFailover publishCacheMongoService;


    @Override
    public void publish(Long batchId, String nextLevelIndexs, BaseAdLevel preLevel) {
        logger.info("PublishAdCampaignGroupJob->publishAdCampaignGroups-开始创建批次：" + batchId + "下的所有AdCampaignGroups.");
        Map<String, AdCampaignGroupLevel> adCampaignGroupLevelMap = JSON.parseObject(publishCacheMongoService.findCacheBlockByType(
                batchId,PublishCacheMongoService.CAMPAIGN),new TypeReference<HashMap<String,AdCampaignGroupLevel>>(){});
        if (checkCampaign(batchId, adCampaignGroupLevelMap)) return;
        PublishBatch publishBatch = publishBatchRdbService.findById(batchId);
        if (checkBatch(batchId,publishBatch)) return;

        //3.遍历adCampaignGroupLevelMap
        String adCampaignGroupIndex;
        AdCampaignGroupLevel adCampaignGroupLevel;
        //每次都取最新的 accessToken
        String accessToken = fbUserRdbService.getTokenByUserId(publishBatch.getUserId());
        for (Map.Entry<String, AdCampaignGroupLevel> entry : adCampaignGroupLevelMap.entrySet()) {
            adCampaignGroupIndex = entry.getKey();
            adCampaignGroupLevel = entry.getValue();
            int adCampaignGroupLevelStatus = adCampaignGroupLevel.getStatus();
            if(adCampaignGroupLevelStatus ==PublishStatus.WAIT_PUPLISH.getCode()|| adCampaignGroupLevelStatus ==PublishStatus.FAIL.getCode()){
                adCampaignGroupLevel.setAccessToken(accessToken);
                AdCampaignParam adCampaignParam= genFbParam(adCampaignGroupLevel,batchId.toString(), PUBLISH_CAMPAIGN);
                adCampaignParam.getEquipment().put("adcampaigngroup_batch_index", adCampaignGroupIndex);
                adCampaignGroupFBService.createCampaignGroup(adCampaignParam);
                publishCacheMongoService.updateStatus(adCampaignGroupLevel.getPublishBatchId(),adCampaignGroupLevel.getAdCampGroupKey(),
                        PublishStatus.PUBLISHING.getCode(),PublishCacheMongoService.CAMPAIGN);
                logger.info("PublishAdCampaignGroupJob->publishAdCampaignGroupCallBack->AdCampaignGroup:"+ adCampaignGroupIndex + ",已经发送。");
            }else if(adCampaignGroupLevelStatus==PublishStatus.SUCCEED.getCode()&&publishBatch.getRetryCnt()>0){
                //如果是成功状态，重试的时候再走一遍流程，因为并不知道该campaign 下的adset 是都成功
                publishAdSetService.publish(batchId, adCampaignGroupIndex, adCampaignGroupLevel);

            }

        }
        logger.info("PublishAdCampaignGroupJob->publishAdCampaignGroups->结束创建批次：" + batchId + "下的所有AdCampaignGroups.");
    }

    @Override
    public void callback(Map<String, String> equipment, Object fbObj) {
        ThreadPool.execute(() -> {
            logger.info("->->AdCampaignGroup创建成功回调开始!");
            PublishStatus publishStatus=null;
            String adCampaignGroupBatchIndex="";
            Long publishBatchId=0L;
            AdCampaignGroupLevel adCampaignGroupLevel=null;
            try {
                logger.debug("->->equipment:" + JSON.toJSONString(equipment));
                logger.debug("->->response:" + JSON.toJSONString(fbObj));
                if (CollectionUtils.isEmpty(equipment)) {
                    logger.error("->->未携带回调所需参数!");
                    return;
                }
                //1.通过equipment中的publishBatchId获取缓存中的AdPublishCache
                publishBatchId = Long.valueOf(equipment.get("publish_batch_id"));

                //2.通过equipment中的adCampaignGroupBatchIndex获取批次下的AdCampaignGroup
                adCampaignGroupBatchIndex = equipment.get("adcampaigngroup_batch_index");
                logger.info("PublishAdSetJob->publishAdCampaignGroupCallBack->AdCampaignGroup创建成功回调开始!adCampaignGroupBatchIndex>>"+adCampaignGroupBatchIndex);

                String adAdCampainGroupLevelJson=publishCacheMongoService.findCacheBlockByTypeAndKey(Long.valueOf(publishBatchId),
                        PublishCacheMongoService.CAMPAIGN,adCampaignGroupBatchIndex);
                adCampaignGroupLevel =JSON.parseObject(adAdCampainGroupLevelJson,AdCampaignGroupLevel.class) ;
                publishStatus= callbackHandler(adCampaignGroupLevel, fbObj);
            } catch (Exception e) {
                logger.error("->->营销活动发布失败", e);
                publishStatus=PublishStatus.FAIL;
            }finally {
                try {
                    publishCacheMongoService.updateStatus(publishBatchId,adCampaignGroupBatchIndex,publishStatus.getCode(),PublishCacheMongoService.CAMPAIGN);
                    publishAdSetService.publish(Long.valueOf(publishBatchId), adCampaignGroupBatchIndex, adCampaignGroupLevel);
                }catch (Exception e1){
                    logger.error("AdCampaignGroupCallBack-> error"+e1.getMessage(),e1);
                }
                logger.info("PublishAdSetJob->publishAdCampaignGroupCallBack->AdCampaignGroup创建成功回调结束!adCampaignGroupBatchIndex>>"+adCampaignGroupBatchIndex);
            }
        });
    }


    @Override
    PublishResultDetailRdbService getPublishResultDetailRdbService() {
        return this.publishResultDetailRdbService;
    }

    @Override
    PublishResultRecordRdbService getPublishResultRecordRdbService() {
        return this.publishResultRecordRdbService;
    }
    /**
     * 重试发布营销活动回调
     * @author Evan
     * @date 2015-07-09
     */

    private AdCampaignParam genFbParam(AdCampaignGroupLevel campaignLevel,String batchId,String objType) {
        AdCampaignParam adCampaignParam = new AdCampaignParam();
        Map<String, String> equipment=new HashMap<>();
        adCampaignParam.setName(campaignLevel.getName());
        adCampaignParam.setStatus(campaignLevel.getCampaignGroupStatus());
        adCampaignParam.setApiPriorityCode(APIPriority.CreateAdCampaignGroups.getCode());
        adCampaignParam.setApiCallbackTargetType(ApiCallBackTarget.AdPublish.AdCampaignGroup.getTargetType());
        adCampaignParam.setObjective(campaignLevel.getObjective());
        adCampaignParam.setPromotedObject(campaignLevel.getPromotedObject());
        adCampaignParam.setCallBack(true);
        adCampaignParam.setFbAccountId(Long.valueOf(campaignLevel.getActAccountId().substring(4)));
        equipment.put("publish_batch_id", batchId);

        adCampaignParam.setAccessToken(campaignLevel.getAccessToken());
        adCampaignParam.setResultClass(FBSingleId.class);
        adCampaignParam.setFbAccountId(Long.valueOf(campaignLevel.getActAccountId().substring(4)));
        adCampaignParam.setEquipment(equipment);
        return adCampaignParam;
    }

    private void saveAdCampaignGroup(AdCampaignGroupLevel adCampaignGroupLevel) {
        AdCampaignGroup adCampaignGroup = new AdCampaignGroup();
        adCampaignGroup.setCampaignGroupId(adCampaignGroupLevel.getCampaignGroupId());
        adCampaignGroup.setFbAccountId(Long.valueOf(adCampaignGroupLevel.getActAccountId().split("act_")[1]));
        adCampaignGroup.setTimezone(adCampaignGroupLevel.getTimeZone());
        adCampaignGroup.setPublishBatchId(adCampaignGroupLevel.getPublishBatchId());
        adCampaignGroup.setPublishBatchName(adCampaignGroupLevel.getPublishBatchName());
        adCampaignGroup.setProductConfId(adCampaignGroupLevel.getProductConfigurationId());
        adCampaignGroup.setFbConnectionObjectId(adCampaignGroupLevel.getFbConnectionObjectId());
        adCampaignGroup.setConnectionObjectName(adCampaignGroupLevel.getFbConnectionObjectName());
        adCampaignGroup.setConfiguration(adCampaignGroupLevel.getConfiguration());
        adCampaignGroup.setName(adCampaignGroupLevel.getName());
        adCampaignGroup.setStartTime(adCampaignGroupLevel.getStartTime());
        adCampaignGroup.setStopTime(adCampaignGroupLevel.getEndTime());
        adCampaignGroup.setCampaignGroupStatus(adCampaignGroupLevel.getCampaignGroupStatus());
        adCampaignGroup.setStartUtcTime(adCampaignGroupLevel.getStartUTCTime());
        adCampaignGroup.setStopUtcTime(adCampaignGroupLevel.getEndUTCTime());
        adCampaignGroup.setUpdatedTime(adCampaignGroupLevel.getUpdateTime());
        adCampaignGroup.setCreatedTime(adCampaignGroupLevel.getCreateTime());
        adCampaignGroup.setUpdatedUtcTime(adCampaignGroupLevel.getUpdateUTCTime());
        adCampaignGroup.setCreatedUtcTime(adCampaignGroupLevel.getCreateUTCTime());
        adCampaignGroup.setObjective(adCampaignGroupLevel.getObjective());
        //walle系统创建的
        adCampaignGroup.setSourceStatus(CampaignGroupResourceStatus.WalleCreate.getCode());
        adCampaignGroupRdbService.save(adCampaignGroup);
    }


    private void publishComplete(Long publishBatchId, Integer status) {
        publishBatchRdbService.updatePublishStatusById(publishBatchId, status);
        logger.debug("PublishJob->publishComplete->批次：" + publishBatchId + "，执行完成，修改批次状态并删除缓存！");

    }
    private PublishStatus callbackHandler(AdCampaignGroupLevel adCampaignGroupLevel,Object fbSingleId) {
        //3.解析response，获取FB响应值
        PublishStatus publishStatus;
        Long campaignGroupId = null;
        String exception = null;
        PublishResultType publishResultType;
        if (fbSingleId instanceof FBSingleId) {
            campaignGroupId = ((FBSingleId) fbSingleId).getId();
            publishStatus = PublishStatus.SUCCEED;
            publishResultType = PublishResultType.campaignGroupSuccess;
        } else {
            exception = fbSingleId.toString();
            publishStatus = PublishStatus.FAIL;
            publishResultType = PublishResultType.campaignGroupFail;
            logger.error("publishAdCampaignGroupCallBack->->fb fail:" + exception);
        }
        //4.根据3的结果回填相关信息
        //4-1 修改AdCampaignGroupLevel的status 1 发布成功  2 发布失败
        adCampaignGroupLevel.setStatus(publishStatus.getCode());
        if (publishStatus.getCode() == PublishStatus.SUCCEED.getCode()) {
            //4-2 如果成功则回填 FB返回的 adCampaignGroupId,更新Id到mongo
            adCampaignGroupLevel.setCampaignGroupId(campaignGroupId);
            publishCacheMongoService.fillFbId(adCampaignGroupLevel.getPublishBatchId(),adCampaignGroupLevel.getAdCampGroupKey(),
                    String.valueOf(campaignGroupId),PublishCacheMongoService.CAMPAIGN);
            //4-3、如果成功AdCampaignGroup入库到ad_campaign_group表
            saveAdCampaignGroup(adCampaignGroupLevel);
        }
        //5、向数据库存储发布结果
        savePublishResult(adCampaignGroupLevel, publishResultType, exception,AD_CAMPAIGN_GROUP);
        return publishStatus;

    }

    private boolean checkCampaign(Long batchId, Map<String, AdCampaignGroupLevel> adCampaignGroupLevelMap) {
        if (CollectionUtils.isEmpty(adCampaignGroupLevelMap)) {
            logger.info("PublishAdCampaignGroupJob->publishAdCampaignGroups->批次:" + batchId + ",不存在待发布的AdCampaignGroup!");
            publishComplete(batchId, PublishStatus.SUCCEED.getCode());
            return  true;
        }
        return false;
    }
}
